package model.map;

import java.util.HashMap;
import java.util.Map;

import model.entity.Avatar;
import utilities.*;

/**
 * The class that will have the data structures for containing the GameMaps in the game.
 * Further, this class will be responsible for switching between GameMaps in the game.
 */
public class World implements Archiveable {
	private Map<String, GameMap> maps;
	private GameMap currentMap;
	private GameMap battleMap;
	
	private Avatar player;
	
	private static World instance;
	
	/* --------------------------------------------------------------------------------
	 * Constructors
	   -------------------------------------------------------------------------------- */
	
	private World()
	{
		//To prevent instantiation.
		//TODO: Probably add shit into this.
		
		maps = new HashMap<String, GameMap>();
	}
		
	/**
	 * Returns the Singleton instance of World.
	 * @return the shared World instance.
	 */
	public static World getInstance()
	{
		if(instance == null) 
		{
			instance = new World();
		}
		return instance;
	}
	
	/* --------------------------------------------------------------------------------
	 * Getters and Setters
	   -------------------------------------------------------------------------------- */
	
	/**
	 * Returns the GameMap that the game is currently using as the active map.
	 * @return
	 */
	public GameMap getCurrentMap()
	{
		return currentMap;
	}
	
	public void setCurrentMap(String mapName)
	{
		
		GameMap newMap = maps.get(mapName);
		newMap.updateFogOfWar();
		
		this.currentMap = newMap;
	}

	public void setCurrentMap(GameMap map)
	{
		if(!maps.containsValue(map))
		{
			addMap(map);
		}
		
		map.updateFogOfWar();
		
		currentMap = map;
		
		
	}

	public Avatar getPlayer()
	{
		return player;
	}
	
	//TODO: Switching maps

	public void setPlayer(Avatar player)
	{
		this.player = player;
	}

	/**
	 * Returns the GameMap that is being used as the BattleMap
	 * @return
	 */
	public GameMap getBattleMap()
	{
		return battleMap;
	}
	
	public void setBattleMap(GameMap battleMap)
	{
		this.battleMap = battleMap;
	}

	
	public boolean isInBattle()
	{
		return (currentMap == battleMap);
	}
	
	/**
	 * Checks if the specified location exists in the current map.
	 * @param loc
	 * @return
	 */
	public boolean currentMapExists(Location loc)
	{
		return currentMap.exists(loc);
	}
	
	public void addMap(GameMap map)
	{
		maps.put(map.getName(), map);
	}
	
	public void updateFogofWar()
	{
		currentMap.updateFogOfWar();
	}
	
	public Archive save() {
		Archive archive = ArchiveFactory.createArchive();
		
		archive.putPrimitiveToArchiveableMap("maps", this.maps);
		
		archive.putOneRelationship("battleMap", this.battleMap);
		archive.putOneRelationship("currentMap", this.currentMap);
		
		archive.putOneRelationship("avatar", this.player);
		
		return archive;
	}
	
	public void load(Archive archive) {
		Map<String, Archive> mapArchives = archive.getPrimitiveToArchiveableMap("maps");
		this.maps = new HashMap<String, GameMap>();
		for (String key : mapArchives.keySet()) {
			Archive a = mapArchives.get(key);
			GameMap map = new GameMap();
			map.load(a);
			this.maps.put(key, map);
		}
		
		this.battleMap = new GameMap();
		this.battleMap.load(archive.getOneRelationship("battleMap"));
		
		this.currentMap = new GameMap();
		this.currentMap.load(archive.getOneRelationship("currentMap"));
		
		this.player = new Avatar();
		this.player.load(archive.getOneRelationship("player"));
	}
}
